package com.aplink.generic.autosync;

import java.util.ArrayList;

import android.accounts.Account;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.SyncResult;
import android.database.Cursor;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.text.TextUtils;

import com.aplink.generic.cache.ContentDataManager;
import com.aplink.generic.cache.MasterConfigManager;
import com.aplink.generic.constant.WebServiceContant;
import com.aplink.generic.constant.type.ActionType;
import com.aplink.generic.contentprovider.DataProvider;
import com.aplink.generic.database.SqlMethods;
import com.aplink.generic.interfaces.OnHttpTaskPostExcute;
import com.aplink.generic.network.GenericFile;
import com.aplink.generic.pojo.ContentData;
import com.aplink.generic.pojo.GenericAttribute;
import com.aplink.generic.pojo.GenericObject;
import com.aplink.generic.pojo.MasterConfig;
import com.aplink.generic.pojo.ObjPropInfo;
import com.aplink.generic.pojo.TableSyncData;
import com.aplink.generic.sharepreference.GenericSharePreference;
import com.aplink.generic.util.LogUtil;
import com.aplink.generic.webservice.DataResult;
import com.aplink.generic.webservice.HttpTask;
import com.aplink.generic.webservice.HttpURL;

public class SyncAdapter extends AbstractThreadedSyncAdapter implements
        OnHttpTaskPostExcute {
	public static final int NOTIFICATION_ID = 1;
	private final ContentResolver mContentResolver;

	private MasterConfig mMasterConfig = null;

	private NotificationManager mNotificationManager;

	private final ArrayList<HttpURL> webserviceURLs;

	public SyncAdapter(final Context context, final boolean autoInitialize) {
		super(context, autoInitialize);
		this.mContentResolver = context.getContentResolver();
		this.webserviceURLs = new ArrayList<HttpURL>();
		mMasterConfig = MasterConfigManager.getInstance().get(
		        GenericSharePreference.getInstance(context)
		                .getMasterConfigFileID());
	}

	private final String buildWhereCondition(final ContentData objPropCol,
	        final GenericObject genericObject) {
		final String table = objPropCol.getMainTable();
		final ObjPropInfo createdTime = objPropCol.getCreatedTime(table);
		final SqlMethods sqlMethods = SqlMethods.getInstance();
		if (createdTime == null) {
			final ObjPropInfo primaryKey = objPropCol.getPrimaryKey(table);
			if (!TextUtils.isEmpty(genericObject.getValue(primaryKey
			        .getFullColumnName()))) {
				final String value = genericObject.getValue(
				        primaryKey.getFullColumnName()).toString();
				sqlMethods.equal(primaryKey, value);
			}
		} else {
			final String value = genericObject.getValue(createdTime
			        .getFullColumnName());
			sqlMethods.equal(createdTime, value);
		}
		return sqlMethods.toString();
	}

	private final void clearSyncStatus(final GenericObject object,
	        final ContentData objPropCol) {
		for (final ObjPropInfo objPropInfo : objPropCol.getSyncStatus()) {
			final String mainTableName = objPropInfo.getTable();
			final ContentData mainTableProperty = ContentData.getObjPropColOf(
			        mainTableName, objPropCol);
			final ObjPropInfo syncStatusColumn = objPropCol
			        .getSyncStatus(mainTableName);

			final Uri uri = Uri
			        .parse(getClearSyncContentURI(this.mMasterConfig) + "/"
			                + mainTableName);

			final SqlMethods whereClause = SqlMethods.getInstance().equal(
			        syncStatusColumn, "delete");

			this.mContentResolver.delete(uri, whereClause.toString(), null);

			final ContentValues values = new ContentValues();
			values.putNull(syncStatusColumn.getColumnName());
			final String where = buildWhereCondition(mainTableProperty, object);
			this.mContentResolver.update(uri, values, where, null);
		}
	}

	private final String getClearSyncContentURI(final MasterConfig masterConfig) {
		if (masterConfig.getAutoSync() != null) {
			return "content://" + masterConfig.getAutoSync().getAuthorities()
			        + "/" + DataProvider.CLEAR_SYNC;
		}
		return null;
	}

	private final String getTableContentURI(final MasterConfig masterConfig) {
		if (masterConfig.getAutoSync() != null) {
			return "content://" + masterConfig.getAutoSync().getAuthorities()
			        + "/" + DataProvider.TABLE;
		}
		return null;
	}

	private final void initHttpURL() {
		for (int i = 0; i < this.mMasterConfig.getWebserviceConfig()
		        .getTableSyncData().size(); i++) {
			final TableSyncData tableSyncData = this.mMasterConfig
			        .getWebserviceConfig().getTableSyncData().valueAt(i);
			if (tableSyncData.isDownloadOnly()) {
				continue;
			}
			final String mainTableName = tableSyncData.getTableName();
			final int contentDataFileId = tableSyncData
			        .getWebserviceFunctionID();
			final ContentData objPropCol = ContentDataManager.getInstance()
			        .get(contentDataFileId);
			final String syncStatusColumn = objPropCol.getSyncStatus(
			        tableSyncData.getTableName()).getFullColumnName();

			final SqlMethods sqlMethods = SqlMethods.getInstance().isNotNull(
			        syncStatusColumn);

			if (tableSyncData.isHavePhoto()) {
				final ObjPropInfo objPrimaryKey = objPropCol
				        .getPrimaryKey(mainTableName);
				final ObjPropInfo imageLocalPath = objPropCol
				        .getImageLocalPath();

				final String distinctColumn = objPrimaryKey.getFullColumnName();

				Uri uri = Uri.parse(getTableContentURI(this.mMasterConfig)
				        + "/" + tableSyncData.getTableName() + "/1");

				ArrayList<String> queriedColumns = new ArrayList<String>();
				for (int j = 0; j < objPropCol.size(); j++) {
					final ObjPropInfo objPropInfo = objPropCol.valueAt(j);
					if (objPropInfo.getTable().equalsIgnoreCase(
					        tableSyncData.getTableName())) {
						queriedColumns.add(objPropInfo.getFullColumnName());
					}
				}

				Cursor cursor = this.mContentResolver.query(uri, queriedColumns
				        .toArray(new String[queriedColumns.size()]), sqlMethods
				        .toString(), null, null);
				final ArrayList<GenericObject> distinctObjectList = selectDistinct(
				        cursor, tableSyncData.getTableName(), objPropCol);

				for (final GenericObject distinctObject : distinctObjectList) {
					final String action = distinctObject.getValue(
					        syncStatusColumn).toString();
					final HttpURL httpURL = new HttpURL();
					httpURL.setContentData(objPropCol)
					        .setDomain(
					                this.mMasterConfig.getWebserviceConfig()
					                        .getWebserviceDomain())
					        .setTable(tableSyncData.getTableName())
					        .setAction(action).setJsonValue(distinctObject);

					if (!objPropCol.getCreatedTimeObject().isEmpty()) {
						if (!action.equalsIgnoreCase("insert")) {
							final String columName = objPropCol.getCreatedTime(
							        mainTableName).getFullColumnName();
							final String value = distinctObject.getValue(
							        columName).toString();
							httpURL.setJsonCondition(value);
						}
					} else {
						LogUtil.error(LogUtil.TAG_AUTO_SYNC,
						        "Created time is required for upload",
						        "Upload", getClass());
						throw new NullPointerException(
						        "Created time is required for upload");
					}

					if (action.equalsIgnoreCase("delete")) {
						httpURL.setWebserviceFunction(objPropCol
						        .getWebserviceFunction().replace(
						                "ImageDataHandle?", "DataHandle?"));
						httpURL.setMethod(WebServiceContant.GET);
					} else {
						httpURL.setWebserviceFunction(objPropCol
						        .getWebserviceFunction().replace("DataHandle?",
						                "ImageDataHandle?"));

						final String value = distinctObject.getValue(
						        distinctColumn).toString();

						final SqlMethods whereClause = SqlMethods
						        .getInstance()
						        .equal(objPrimaryKey.getFullColumnName(), value);

						final ArrayList<GenericFile> listUploadFiles = new ArrayList<GenericFile>();
						final ArrayList<GenericFile> listDeleteFile = new ArrayList<GenericFile>();

						final ObjPropInfo fileSyncStatus = objPropCol
						        .getSyncStatus(imageLocalPath.getTable());

						whereClause.and().isNotNull(
						        fileSyncStatus.getFullColumnName());

						uri = Uri.parse(getTableContentURI(this.mMasterConfig)
						        + "/" + objPropCol.getJoins());
						queriedColumns = new ArrayList<String>();
						for (int j = 0; j < objPropCol.size(); j++) {
							final ObjPropInfo objPropInfo = objPropCol
							        .valueAt(j);
							if (objPropInfo.getTable().equalsIgnoreCase(
							        fileSyncStatus.getTable())) {
								queriedColumns.add(objPropInfo
								        .getFullColumnName());
							}
						}

						cursor = this.mContentResolver.query(uri,
						        queriedColumns
						                .toArray(new String[queriedColumns
						                        .size()]), whereClause
						                .toString(), null, null);

						final ArrayList<GenericObject> images = selectDistinct(
						        cursor, fileSyncStatus.getTable(), objPropCol);

						for (final GenericObject image : images) {
							final String syncStatus = image.getValue(
							        fileSyncStatus.getFullColumnName())
							        .toString();

							final String localPath = image.getValue(
							        imageLocalPath.getFullColumnName())
							        .toString();
							final GenericFile file = new GenericFile();
							file.setLocalPath(localPath);
							file.setFileNameByURl(localPath);
							if (syncStatus.equalsIgnoreCase("insert")) {
								listUploadFiles.add(file);
							} else {
								listDeleteFile.add(file);
							}
						}

						httpURL.setListUploadFiles(listUploadFiles);
						httpURL.setListDeleteFiles(listDeleteFile);
						httpURL.setMethod(WebServiceContant.POST);
					}
					this.webserviceURLs.add(httpURL);
				}
			} else {
				final Uri uri = Uri
				        .parse(getTableContentURI(this.mMasterConfig) + "/"
				                + tableSyncData.getTableName());

				final ArrayList<String> queriedColumns = new ArrayList<String>();
				for (int j = 0; j < objPropCol.size(); j++) {
					final ObjPropInfo objPropInfo = objPropCol.valueAt(j);
					if (objPropInfo.getTable().equalsIgnoreCase(
					        tableSyncData.getTableName())) {
						queriedColumns.add(objPropInfo.getFullColumnName());
					}
				}

				final Cursor cursor = this.mContentResolver
				        .query(uri, queriedColumns
				                .toArray(new String[queriedColumns.size()]),
				                sqlMethods.toString(), null, null);
				final ArrayList<GenericObject> genericObjects = selectDistinct(
				        cursor, tableSyncData.getTableName(), objPropCol);

				for (final GenericObject obj : genericObjects) {
					final String action = obj.getValue(syncStatusColumn)
					        .toString();
					final String domain = this.mMasterConfig
					        .getWebserviceConfig().getWebserviceDomain();
					final String webserviceFunction = objPropCol
					        .getWebserviceFunction();
					final HttpURL httpURL = new HttpURL();
					httpURL.setContentData(objPropCol).setDomain(domain)
					        .setWebserviceFunction(webserviceFunction)
					        .setTable(tableSyncData.getTableName())
					        .setAction(action).setJsonValue(obj);
					if (!objPropCol.getCreatedTimeObject().isEmpty()) {
						if (!action.equalsIgnoreCase("insert")) {
							final String columName = objPropCol
							        .getCreatedTimeObject().get(0)
							        .getFullColumnName();
							final String value = obj.getValue(columName)
							        .toString();
							httpURL.setJsonCondition(value);
						}
					}
					httpURL.setMethod(WebServiceContant.GET);
					this.webserviceURLs.add(httpURL);
				}
			}
		}
	}

	private final boolean isNetworkAvailable() {
		final ConnectivityManager cm = (ConnectivityManager) getContext()
		        .getSystemService(Context.CONNECTIVITY_SERVICE);
		final NetworkInfo networkInfo = cm.getActiveNetworkInfo();
		if ((networkInfo != null) && networkInfo.isConnected()) {
			return true;
		}
		return false;
	}

	@Override
	public void onPerformSync(final Account account, final Bundle extras,
	        final String authority, final ContentProviderClient provider,
	        final SyncResult syncResult) {
		boolean isNetworkAvailable = isNetworkAvailable();
		final boolean isPending = GenericSharePreference.getInstance(
		        getContext()).isAutoSyncPending();
		final boolean isDatabaseModified = GenericSharePreference.getInstance(
		        getContext()).isDatabaseModified();
		final boolean isEnableAutoSync = GenericSharePreference.getInstance(
		        getContext()).isEnableAutoSync();
		final boolean isStartAutoSync = GenericSharePreference.getInstance(
		        getContext()).isStartAutoSync();
		LogUtil.info(
		        LogUtil.TAG_AUTO_SYNC,
		        String.format(
		                "isNetworkAvailable=%s, isPending=%s, isDatabaseModified=%s, isEnableAutoSync=%s, isStartAutoSync=%s",
		                isNetworkAvailable, isPending, isDatabaseModified,
		                isEnableAutoSync, isStartAutoSync), "onPerformSync",
		        getClass());
		if (isNetworkAvailable) {
			if (isEnableAutoSync && isStartAutoSync && !isPending
			        && isDatabaseModified) {
				LogUtil.info(LogUtil.TAG_AUTO_SYNC, "Auto-Sync is start",
				        "onPerformSync", getClass());
				initHttpURL();
				if (!this.webserviceURLs.isEmpty()) {
					GenericSharePreference.getInstance(getContext())
					        .setAutoSyncPending(true);
					final HttpURL[] urls = this.webserviceURLs
					        .toArray(new HttpURL[this.webserviceURLs.size()]);
					final HttpTask httpTask = new HttpTask(ActionType.AUTO_SYNC);
					httpTask.setHttpTaskPostExcute(this);
					httpTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
					        urls);
				}
			}
		}
	}

	@Override
	public void onPostExecute(final ArrayList<DataResult> result) {
		if (!result.isEmpty()) {
			GenericSharePreference.getInstance(getContext())
			        .setAutoSyncPending(false);
			GenericSharePreference.getInstance(getContext())
			        .setDatabaseModified(false);
			GenericSharePreference.getInstance().setStartAutoSync(false);
			SyncUtils.getInstance().disableAutoSync();
			sendNotification();
			for (final DataResult dataResult : result) {
				final ContentData tableSyncData = dataResult.getContentData();
				final GenericObject genericObject = dataResult
				        .getGenericObject();
				clearSyncStatus(genericObject, tableSyncData);
			}
		}
	}

	private final ArrayList<GenericObject> selectDistinct(Cursor cursor,
	        final String table, final ContentData contentData) {
		final ArrayList<GenericObject> genericObjectList = new ArrayList<GenericObject>();
		if (cursor != null) {
			cursor.moveToFirst();
			final int numRows = cursor.getCount();
			for (int i = 0; i < numRows; ++i) {
				final GenericObject genericObject = new GenericObject();
				genericObject.setTableName(table);
				for (int j = 0; j < contentData.size(); j++) {
					final ObjPropInfo objPropInfo = contentData.valueAt(j);
					if (objPropInfo.getTable().equalsIgnoreCase(table)) {
						final String name = objPropInfo.getColumnName();
						String value = cursor.getString(cursor
						        .getColumnIndex(name));
						if (value == null) {
							value = "";
						}
						final GenericAttribute attribute = GenericAttribute
						        .obtain(value, objPropInfo);
						genericObject.put(objPropInfo.getFullColumnName(),
						        attribute);
					}
				}
				genericObjectList.add(genericObject);
				cursor.moveToNext();
			}
			cursor.close();
			cursor = null;
			return genericObjectList;
		}
		return null;
	}

	private final void sendNotification() {
		this.mNotificationManager = (NotificationManager) getContext()
		        .getSystemService(Context.NOTIFICATION_SERVICE);
		final NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
		        getContext())
		        .setAutoCancel(true)
		        .setContentTitle(
		                this.mMasterConfig.getAutoSync().getContentTitle())
		        .setSmallIcon(this.mMasterConfig.getAutoSync().getIconID())
		        .setDefaults(
		                Notification.DEFAULT_LIGHTS
		                        | Notification.DEFAULT_SOUND)
		        .setContentText(this.mMasterConfig.getAutoSync().getMessage());
		this.mNotificationManager.notify(SyncAdapter.NOTIFICATION_ID,
		        mBuilder.build());
	}
}
